﻿// <copyright file="Point3D.cs" company="Axelerate Solutions S.A.">
// Copyright (c) Axelerate Solutions S.A.  All rights reserved.
// </copyright>

namespace Axelerate.Silverlight3D.Media.Media3D
{
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.ComponentModel;

    /// <summary>
    /// File name: Point3D.cs
    /// Company: Axelerate Solutions S.A.
    /// Created By: Daniel Rojas
    /// Created Date: 01/30/2008 15:30:00 h.
    /// Description: Defines an x-, y-, and z-coordinate in 3-D space.
    ///   <Modifications>
    ///   </Modifications>
    /// </summary>
    [TypeConverterAttribute(typeof(Point3DConverter))]
    public struct Point3D : IFormattable
    {
        #region Fields
        /// <summary>
        ///  The x-coordinate of this Point3D.
        /// </summary>
        private double mX;

        /// <summary>
        ///  The y-coordinate of this Point3D.
        /// </summary>
        private double mY;

        /// <summary>
        ///  The z-coordinate of this Point3D.
        /// </summary>
        private double mZ;
        #endregion

        #region Properties
        /// <summary>
        ///  The x-coordinate of this Point3D.
        /// </summary>
        public double X
        {
            get
            {
                return mX;
            }
            set
            {
                mX = value;
            }
        }
        
        /// <summary>
        ///  The y-coordinate of this Point3D.
        /// </summary>
        public double Y
        {
            get
            {
                return mY;
            }
            set
            {
                mY = value;   
            }
        }

        /// <summary>
        ///  The z-coordinate of this Point3D.
        /// </summary>
        public double Z
        {
            get
            {
                return mZ;
            }
            set
            {
                mZ = value;
            }
        }

        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the Point3D structure.
        /// </summary>
        /// <param name="x"><typeparamref name="System.Double"/>The X value of the new Point3D structure.</param>
        /// <param name="y"><typeparamref name="System.Double"/>The Y value of the new Point3D structure.</param>
        /// <param name="z"><typeparamref name="System.Double"/>The Z value of the new Point3D structure.</param>
        public Point3D(double x, double y, double z)
        {
            mX = x;
            mY = y;
            mZ = z;
        }
        #endregion

        #region IFormattable Members
        /// <summary>
        /// Convert the Point3D to his string representation.
        /// </summary>
        /// <param name="format">ignored for this class</param>
        /// <param name="formatProvider">ignored for this class</param>
        /// <returns>return the respective string</returns>
        public string ToString(string format, IFormatProvider formatProvider)
        {
            return (String.Format(formatProvider, format, X, Y, Z));   
        }

        /// <summary>
        /// Convert the Point3D to his string representation.
        /// </summary>
        /// <param name="provider">IFormatProvider for this convertion</param>
        /// <returns>return the respective string</returns>
        public string ToString(IFormatProvider provider)
        {
            return (String.Format(provider, "{0},{1},{2}", X, Y, Z));   
        }

        /// <summary>
        /// Convert the Point3D to his string representation.
        /// </summary>
        /// <returns>return the respective string</returns>
        public override string ToString()
        {
            return (String.Format("{0},{1},{2}", X, Y, Z));
        }

        #endregion

        #region operators overrides
        /// <summary>
        /// Adds a Point3D structure to a Vector3D and returns the result as a Point3D structure.
        /// </summary>
        /// <param name="point"><typeparamref name="Axelerate.Silverlight3D.Media.Media3D.Point3D"/>The Point3D structure to add.</param>
        /// <param name="vector"><typeparamref name="Axelerate.Silverlight3D.Media.Media3D.Vector3D"/>The Vector3D structure to add.</param>
        /// <returns><typeparamref name="Axelerate.Silverlight3D.Media.Media3D.Point3D"/>The sum of point and vector.</returns>
        public static Point3D operator +(Point3D point, Vector3D vector)
        {
            return Point3D.Add(point, vector);
        }

        /// <summary>
        /// Compares two Point3D structures for equality.
        /// </summary>
        /// <param name="point1">The first Point3D structure to compare.</param>
        /// <param name="point2">The second Point3D structure to compare.</param>
        /// <returns>true if the X, Y, and Z values for point1 and point2 are equal; otherwise, false.</returns>
        /// /// <remarks>
        /// Two Point3D structures are not equal if the values of their X, Y, and Z properties are different.
        ///
        /// Because the value of Doubles can lose precision when arithmetic operations are performed on them, a comparison between two Doubles that are logically equal might fail.
        /// </remarks>
        public static bool operator ==(Point3D point1, Point3D point2)
        {
            return Point3D.Equals(point1, point2);
        }

        /// <summary>
        /// Converts a Point3D structure into a Vector3D structure.
        /// </summary>
        /// <param name="point">The point to convert.</param>
        /// <returns>The result of converting point.</returns>
        public static explicit operator Vector3D(Point3D point)
        {
            return new Vector3D(point.X, point.Y, point.Z);
        }

        /// <summary>
        /// Compares two Point3D structures for inequality.
        /// </summary>
        /// <param name="point1">The first Point3D structure to compare.</param>
        /// <param name="point2">The second Point3D structure to compare.</param>
        /// <returns>true if the X, Y, and Z coordinates of point1 and point2 are different; otherwise, false.</returns>
        /// <remarks>
        /// Two Point3D structures are not equal if the values of their X, Y, and Z properties are different.
        ///
        /// Because the value of Doubles can lose precision when arithmetic operations are performed on them, a comparison between two Doubles that are logically equal might fail.
        /// </remarks>
        public static bool operator !=(Point3D point1, Point3D point2)
        {
            return !Point3D.Equals(point1, point2);
        }

        /// <summary>
        /// Subtracts a Point3D structure from a Point3D structure and returns the result as a Vector3D structure.
        /// </summary>
        /// <param name="point1">The Point3D structure on which to perform subtraction.</param>
        /// <param name="point2">The Point3D structure to subtract from point1.</param>
        /// <returns>A Vector3D structure that represents the difference between point1 and point2.</returns>
        public static Vector3D operator -(Point3D point1, Point3D point2)
        {
            return Point3D.Subtract(point1, point2);
        }

        /// <summary>
        /// Subtracts a Vector3D structure from a Point3D structure and returns the result as a Point3D structure.
        /// </summary>
        /// <param name="point">The Point3D structure from which to subtract vector.</param>
        /// <param name="vector">The Vector3D structure to subtract from point.</param>
        /// <returns>The changed Point3D structure, the result of subtracting vector from point.</returns>
        public static Point3D operator -(Point3D point, Vector3D vector)
        {
            return Point3D.Subtract(point, vector);
        }

        #endregion

        #region Methods
        /// <summary>
        /// Adds a Point3D structure to a Vector3D and returns the result as a Point3D structure.
        /// </summary>
        /// <param name="point"><typeparamref name="Axelerate.Silverlight3D.Media.Media3D.Point3D"/>The Point3D structure to add.</param>
        /// <param name="vector"><typeparamref name="Axelerate.Silverlight3D.Media.Media3D.Vector3D"/>The Vector3D structure to add.</param>
        /// <returns><typeparamref name="Axelerate.Silverlight3D.Media.Media3D.Point3D"/>The sum of point and vector.</returns>
        /// <example>
        /// The following example shows how to add a Point3D and a Vector3D.
        /// <code>
        ///                  // Translates a Point3D by a Vector3D using the static Add method.
        ///                  // Returns a Point3D.
        ///  
        ///                  Point3D point1 = new Point3D(10, 5, 1);
        ///                  Vector3D vector1 = new Vector3D(20, 30, 40);
        ///                  Point3D pointResult = new Point3D();
        ///  
        ///                  pointResult = Point3D.Add(point1, vector1);
        ///                  // pointResult is equal to (30, 35, 41)
        ///  
        ///                  // Displaying Results
        ///                  syntaxString = "pointResult = Point3D.Add(point1, vector1);";
        ///                  resultType = "Point3D";
        ///                  operationString = "Adding a 3D Point and a 3D Vector";
        ///                  ShowResults(pointResult.ToString(), syntaxString, resultType, operationString);
        /// </code>
        /// </example>
        public static Point3D Add(Point3D point, Vector3D vector)
        {
            return new Point3D(point.X + vector.X, point.Y + vector.Y, point.Z + vector.Z);
        }
        
        /// <summary>
        /// Determines whether the specified Object is a Point3D structure and if so, whether the X, Y, and Z properties of the specified Object are equal to the X, Y, and Z properties of this Point3D structure.
        /// </summary>
        /// <param name="obj">The Object to compare.</param>
        /// <returns>true if instances are equal; otherwise, false.</returns>
        /// /// <remarks>
        /// Two Point3D structures are not equal if the values of their X, Y, and Z properties are different.
        ///
        /// Because the value of Doubles can lose precision when arithmetic operations are performed on them, a comparison between two Doubles that are logically equal might fail.
        /// </remarks>
        public override bool Equals(object obj)
        {
            if (obj is Point3D)
            {
                return this.Equals((Point3D)obj);
            }
            return false;
        }

        /// <summary>
        /// Compares two Point3D structures for equality.
        /// </summary>
        /// <param name="value">The instance of Point3D to compare to this instance.</param>
        /// <returns>true if instances are equal; otherwise, false.</returns>
        /// /// <remarks>
        /// Two Point3D structures are not equal if the values of their X, Y, and Z properties are different.
        ///
        /// Because the value of Doubles can lose precision when arithmetic operations are performed on them, a comparison between two Doubles that are logically equal might fail.
        /// </remarks>
        public bool Equals(Point3D value)
        {
            return this == value;
        }
        
        /// <summary>
        /// Compares two Point3D structures for equality.
        /// </summary>
        /// <param name="point1">The first Point3D structure to compare.</param>
        /// <param name="point2">The second Point3D structure to compare.</param>
        /// <returns>true if the X, Y, and Z values for point1 and point2 are equal; otherwise, false.</returns>
        /// /// <remarks>
        /// Two Point3D structures are not equal if the values of their X, Y, and Z properties are different.
        ///
        /// Because the value of Doubles can lose precision when arithmetic operations are performed on them, a comparison between two Doubles that are logically equal might fail.
        /// </remarks>
        public static bool Equals(Point3D point1, Point3D point2)
        {
            return point1.X == point2.X && point1.Y == point2.Y && point1.Z == point2.Z;
        }
        
        /// <summary>
        /// Gets a hash code for this Point3D structure.
        /// </summary>
        /// <returns>A hash code for this Point3D structure.</returns>
        public override int GetHashCode()
        {
            return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode();
        }


        public static Point3D Multiply(Point3D point, Matrix3D matrix)
        {
            // TODO: do the multiplication
            
            return new Point3D();
        }

        /// <summary>
        /// Changes the X, Y, and Z values of this Point3D structure by the specified amounts.
        /// </summary>
        /// <param name="offsetX">The amount to change the X coordinate of this Point3D structure.</param>
        /// <param name="offsetY">The amount to change the Y coordinate of this Point3D structure.</param>
        /// <param name="offsetZ">The amount to change the Z coordinate of this Point3D structure.</param>
        /// <remarks>This operation is equivalent to adding a Vector3D structure to a Point3D structure with the corresponding X, Y, and Z values.</remarks>
        public void Offset(double offsetX, double offsetY, double offsetZ)
        {
            this.mX += offsetX;
            this.mY += offsetY;
            this.mZ += offsetZ;
        }

        /// <summary>
        /// Converts a String representation of a 3-D point into the equivalent Point3D structure.
        /// </summary>
        /// <param name="source">The String representation of the 3-D point.</param>
        /// <returns>The equivalent Point3D structure.</returns>
        public static Point3D Parse(string source)
        {
            string[] parts = source.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length == 3)
            {
                double x = double.Parse(parts[0]);
                double y = double.Parse(parts[1]);
                double z = double.Parse(parts[2]);
                return new Point3D(x, y, z);
            }
            throw new Exception("Invalid source string");
        }

        /// <summary>
        /// Subtracts a Point3D structure from a Point3D structure and returns the result as a Vector3D structure.
        /// </summary>
        /// <param name="point1">The Point3D structure on which to perform subtraction.</param>
        /// <param name="point2">The Point3D structure to subtract from point1.</param>
        /// <returns>A Vector3D structure that represents the difference between point1 and point2.</returns>
        public static Vector3D Subtract(Point3D point1, Point3D point2)
        {
            return new Vector3D(point1.X - point2.X, point1.Y - point2.Y, point1.Z - point2.Z);
        }

        /// <summary>
        /// Subtracts a Vector3D structure from a Point3D structure and returns the result as a Point3D structure.
        /// </summary>
        /// <param name="point">The Point3D structure from which to subtract vector.</param>
        /// <param name="vector">The Vector3D structure to subtract from point.</param>
        /// <returns>A changed Point3D structure, the result of subtracting vector from point.</returns>
        public static Point3D Subtract(Point3D point, Vector3D vector)
        {
            return new Point3D(point.X - vector.X, point.Y - vector.Y, point.Z - vector.Z);
        }

        #endregion

    }



}
